<?php
/*
Name: Carbon Paper Vote Profiler
Version: 0.1
Author: Bertrand Scarpa
Description: Increase gradually user votes for any posts following goals defined by writer 
Use a distribution profile (Poisson's law)  
*/

Define('EULER',0.5772156);

include_once(INCLUDEPATH . 'dateinterval.php');
include_once(INCLUDEPATH . 'logfile.php');


if(!function_exists('vp_process_vote')) {
	class VoteProfiler{
		public $included_types;
		public  $duration;								// Duration of push (hours);
		public  $step;								// Cronjob Execution frequency (hours)
		public  $r;										// Wished marge for each occurence (%) 
		public  $k;
		public $data;									
		
		private $posts;
		private $post;
		private $goals;
		private $title;
		private $goal;
		private $density;
		private $gain;
		private $score;
		private $cumul;
		
		public function __construct(){
		}
		
		// Excecuted by $step of time via cronjob :
		public function vp_process_vote(){
			// Test if plugin reaction-button is registered
			$current_plugins = get_option('active_plugins');
			if (in_array('reaction-buttons/reaction_buttons.php', $current_plugins)) {
				// Get the working set of posts included in $duration
				$this->vp_get_posts();
				// For each post of the working set :
				foreach ($this->posts as $this->post) {
					// Get global goals of the votes panel defined by user
					if ($this->vp_get_goals()){
						foreach ($this->goals as $this->title => $this->goal) {
							$this->goal = 150;
							// Get Score
							$this->vp_get_score();
							// Generate gain
							$this->vp_generate_gain();
							// Update score
							$this->vp_update();
						}
					}
				}
			}
		}
	
		private function vp_get_posts(){
			global $wpdb;
			$included_types_clause = !empty($this->included_types) ? 
					" AND post_type IN('" . implode("','",$this->included_types) . "') " : ""; 
			// Get the working set of posts included in the push vote $duration 
			$this->posts = $wpdb->get_results("SELECT ID, post_title, post_type, post_date " .
					" FROM " . $wpdb->posts .
					" WHERE TIME_TO_SEC(TIMEDIFF(Now(),post_date)) / 3600 <= " . $this->duration . 
					" AND post_status = 'publish'" . $included_types_clause);
			//print_r($this->posts);
		}
		
		
		private function vp_get_goals(){
			global $wpdb;
			// Get global goals of the votes panel defined by user
			$goals = $wpdb->get_var("SELECT meta_value from $wpdb->postmeta where post_id=". $this->post->ID ." and meta_key = '_metabox_voteprofiler'");
			$this->goals = unserialize($goals);
			return $goals;
		}
		

		private function vp_get_score(){
			global $wpdb;
			$score = $wpdb->get_var("SELECT meta_value from $wpdb->postmeta where post_id =" . $this->post->ID . " and meta_key = '$this->title';");
			if(is_null($score)){  // => create a new score line;
				$this->score = 0;
				$wpdb->insert($wpdb->postmeta, array('post_id' => $this->post->ID, 'meta_key' => $this->title, 'meta_value' => $this->score));				
			}else{
				$this->score = $score;
			}
		}	


		private function vp_generate_gain(){
			$step = 1;
			$lim0 = 0.002; // empirical;
			$duration = 360;
			$goal = 360;
			$step = 1;
			$scale = $duration * (1/log(-log($lim0)/$lim0));
			$location = $scale * log(-log($lim0));
			//$scale = 1; $location = 3;
			$mode = $location + $scale * EULER;
			$stddev = pi()/sqrt(6)*$scale;
			echo "scale: $scale location: $location<br>";
			$rand = array();
//			while (count($rand) <= $goal) {
//			    $r = mt_rand(1,$duration+1); 
//			    if ( !in_array($r,$rand) ) {
//			        $rand[] = $r;
//			    }
//			}
			 function uniqueRand($n, $min = 0, $max = null){
			 	if($max === null)
			  		$max = getrandmax();
			  	$array = range($min, $max);
			  	$return = array();
			  	$keys = array_rand($array, $n);
			  	foreach($keys as $key)
			   		$return[] = $array[$key];
			  	return $return;
			 }
			 $rand = uniqueRand($goal,1,$duration+1 );
			
			$cumul = 0;
			for ($k = 0; $k <= $goal; $k+=$step){
				$u = $k;
				$z = exp(($location-$u)/$scale);
				$gumbel = exp(-$z)/$scale;  
				$etalon = number_format($gumbel,6,'.','');
				$cumul += (float)$etalon;
			}
			$datatmp = array_fill(0,$duration+1,0);
			for ($k = 1; $k <= $goal; $k+=$step){
				$u = (rand(1,$duration+1) / ($duration + 2));
				//$u = (float)rand(0,$duration)/getrandmax(); 
				//mt_srand();
				//$u = round(360 * mt_rand()/mt_getrandmax()); 
				//$u = $rand[$k]/($duration+1); 
				//$u = $k/360;
				//$z = exp(($location-$u)/$scale);
				//$gumbel = exp(-$z)/($scale * $cumul);  
				//$gumbel = exp($z)*exp(-exp(-$z))/$scale;  
				$gumbel = $location - $scale * log(log(1/number_format($u,6)));
				//$gain = ceil($gumbel); 
				//$etalon = round(number_format($gumbel,6,'.','')* $duration);
				//$etalon = number_format($gumbel,6,'.','');
				$etalon = round($gumbel);
				//echo "random: $u<br>";
				echo "index:$k rand:$rand[$k]: proba: $u z:$z repartition:$gumbel etalon:$etalon<br>";
				//if ($etalon > 0)
				//$datatmp[] = (float)$etalon;
				$datatmp[$etalon] += 1;
				//$datatmp[$u] +=1;
				//echo "$k : $u : $gumbel<br>";
				//$z = exp(($location-$k)/$scale);
				//$gumbel = exp(-$z)*$z/$scale;  
				//$gumbel = exp($z)*exp(-exp(-$z))/$scale;  
				//for ($i=0; $i < $this->step;$i++) $this->data01[]=null;
			}
			//print_r($datatmp);
			
//			foreach ($datatmp as $key => $row) {
// 			   $array_u[$key]  = $row['u'];
//    		   $array_gumbel[$key] = $row['gumbel'];
//			}
//			array_multisort($array_u, SORT_ASC, $array_gumbel, SORT_ASC, $datatmp);
//
//			foreach ($datatmp as $key => $row) {
// 			   $	array_u[$key]  = $row['u'];
//    		   $array_gumbel[$key] = $row['gumbel'];
//			}
			//print_r($array_gumbel);
			echo "sum gain: " . array_sum($datatmp);
			//echo "sum gain: $cumul";
			
			$this->data02 = $datatmp;
			//$this->data02 = $datatmp;
			$this->vp_display();
//					
//			$this->k = 0;
//			$this->cumul = 0;
//			$this->cumul01 = 0;
//			// Set a randomized marge (+/- r%)
//			$this->vp_set_marge();
//			if ($this->mode == 'debug'){
//				if ($this->title == '_reaction_buttons_FAKE'){
//					for ($k = 0; $k <= $this->duration; $k+=$this->step){
//						$this->k = $k;
//						// Set gain
//						$this->vp_set_gain();
//						
//						$this->data01[]= (float)number_format($this->density,6,'.','');
//						for ($i=0; $i < $this->step;$i++) $this->data01[]=null;
//						
//						$this->data02[]= $this->gain;
//						for ($i=0; $i < $this->step;$i++) $this->data02[]=null;
//					}
//					echo "Sum gain: " . array_sum($this->data02);
//					echo "Sum density:$this->cumul01";
//					//print_r($this->data02);
//					$this->vp_display();
//				}
//			}else{
//				$this->vp_set_occurence();
//				$this->vp_set_gain();
//			}
		}		

		private function vp_set_marge(){
			// Compute randomized marge at +/- $r percent
			$this->marge = 1-$this->r/100+(Rand(0,10)*$this->r/500);
		}


		private function vp_set_occurence(){
			// Define the current occurence $this->k 
			$now = new Datetime('now');
			$post_date = new Datetime ($this->post->post_date);
			$diff = date_diff($now, $post_date);
			$this->k  = $diff->format('%d') * 24 + $diff->format('%h');
		}
		
		
		private function vp_set_gain(){
			$gain = 0;$this->gain = 0;
			// Compute gains during the cron step from the occurence $this->k during $this->step
			for ($i=0; $i < $this->step;$i++){

				// Compute density for the occurence
				$this->vp_set_density();

				// Gain of the $k occurence :			
				$gain = $this->goal * $this->density * $this->marge * 1.005; 
				//$this->gain = round($gain);				
				
				// Garbage Collector : discrimination of low gain
				$gain_tmp = floor($gain);
				$this->cumul01 += $this->density;
				$this->cumul += $gain - floor($gain);
				if ($this->cumul > 1){
					echo "$this->k : $i : $gain : $this->cumul <br>";
					//exit();
					$this->cumul -= floor($this->cumul);
					$gain_tmp = ceil($gain);
				}
				$this->gain += $gain_tmp;
				$this->k += 1;
			}	
		}
		
		private function vp_set_density(){
			// Define the Gumbel Masse Function distribution profile depending of $this->duration and occurence $this->k
			$lim0 = 0.001; // empirical;
			$scale = $this->duration * (1/log(-log($lim0)/$lim0));
			$location = $scale * log(-log($lim0));
			$z = exp(($location-$this->k)/$scale);
			$this->density = exp(-$z)*$z/$scale;  
//			if ($this->k >= 100 && $this->k <= 10){
//			echo "$this->k : $this->density<br>";
//			$this->data02[] = null;
//			$this->data02[] = $this->density;
//			$this->data02[] = null;
//			}
		}
		
		
		private function vp_update(){
			global $wpdb;
			$this->score += $this->gain;
			//echo "title:$this->title	occurence:$this->k	density: $this->density gain: $this->gain score:$this->score<br>";
			//if ($this->score <= $this->gain) // security: limit score to the value of the final gain
			//$wpdb->update($wpdb->postmeta, array('meta_value' => $this->score), array(post_id => $this->post->ID, 'meta_key'  => $this->title));
		}
		
		public function vp_display(){
		
			//Chart 01
			$title01 = new title("Density");
			$x01 = new y_axis();
			$y01 = new y_axis();
			$line01 = new line();

			$x01->set_range( 0, 400, 50);
			$y01->set_range( 0, 0.01, 0.001);
			$line01->set_values( $this->data01);

			$chart01 = new open_flash_chart();
			$chart01->set_title( $title01 );
			$chart01->set_x_axis( $x01 );
			$chart01->set_y_axis( $y01 );
			$chart01->add_element( $line01 );
			
			$data01=$chart01->toPrettyString();
			
			//Chart 02
			$title02 = new title("Gain");
			$x02 = new y_axis();
			$y02 = new y_axis();
			$line02 = new bar_filled('#E2D66A','#577261');
			
			$x02->set_range( 0, 400, 40);
			$y02->set_range( 0, 30, 1);
			//$y02->set_range( 0, 0.04, 0.005);
			$line02->set_values( $this->data02);

			$chart02 = new open_flash_chart();
			$chart02->set_title( $title02 );
			$chart02->set_x_axis( $x02 );
			$chart02->set_y_axis( $y02 );
			$chart02->add_element( $line02 );

			$data02=$chart02->toPrettyString();
			
			include_once(INCLUDEPATH . '_script/_chart.php');
		}
	}

//	$voteprofiler = new VoteProfiler();
//	$voteprofiler->included_types = array('posts','buzz');
//	$voteprofiler->duration = 360;
//	$voteprofiler->step = 20;
//	$voteprofiler->r = 100;
//	$voteprofiler->vp_process_vote();

	function vp_test_vote(){
		$included_types = array('posts','buzz');
		$duration =360; // hours
		$step = 5; //hours
		$r = 100; //%
		$n = round($duration/$step);	
		
		$voteprofiler = new VoteProfiler();
		$voteprofiler->mode = 'debug';
		$voteprofiler->included_types = $included_types;
		$voteprofiler->duration = $duration;
		$voteprofiler->step = $step;
		$voteprofiler->r = $r;

		$voteprofiler->vp_process_vote();
	}
	vp_test_vote();
	
}

function fact($i){
	if($i == 0) return 1; // stop condition    
	else return $i*fact($i-1); 
}

exit();
?>